import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.charset.StandardCharsets;

public class Server {
    private DatagramSocket socket = null;


    public Server(int port) throws SocketException {
        //这么写就是让手动指定服务器端口
        socket = new DatagramSocket(port);
        //这么写就是系统自动分配端口
        //socket = new DatagramSocket();

    }


    public void start() throws IOException {
        //通过这个方法来启动服务器
        System.out.println("启动服务器");
        while(true){
            //读取数据并且解析
            DatagramPacket requestPacket = new DatagramPacket(new byte[4096], 4096);
            socket.receive(requestPacket);//客户端请求来之前就会自动阻塞
            //当前完成 receive 之后, 数据是以二进制的形式存储到 DatagramPacket 中了
            //要想把这里的数据给显示出来，还需要把二进制转为字符串
            String request = new String(requestPacket.getData(), 0 , requestPacket.getLength());
            //根据请求计算响应
            String response = process(request);
            //把响应写回到客户端
            //这里的response.getBytes().length和直接response.length的区别是前者是或者字节长度，后者是获取字符串长度，在网络传输中，
            // 使用字节
            DatagramPacket responsePacket = new DatagramPacket(response.getBytes(), response.getBytes().length ,
                    requestPacket.getSocketAddress());
            socket.send(responsePacket);

            System.out.printf("[%s:%d] req = %s , resp = %s \n" , responsePacket.getAddress().toString() ,
                    responsePacket.getPort() , request , response);


            //为什么不写close也不会资源泄露,socket是属于文件描述符表,当socket不需要使用时，会自动销毁，此时文件描述符表也会自动销毁
            // (PCB都销毁了)

            //在代码当中,只有不停的打开程序而不关闭程序才会出现泄露,在一个进程运行的过程中,不断积累打开的文件,逐渐消耗掉文件描述符表的内容

            //但是如果生命周期很短, 打开没多久就关闭了，就谈不上泄露
        }
    }

    public String process(String request) {
        return request;
    }

    public static void main(String[] args) throws IOException {
        Server server = new Server(9090);
        server.start();
    }
}
